/*
JWT 中间件
实现 token 的分配和验证
*/

package middleware

import (
    "errors"
    "net/http"
    "strings"
    "sync"

    "gitee.com/keeving/quanta-test-cat/utils"
    "gitee.com/keeving/quanta-test-cat/utils/errmsg"
    "github.com/gin-gonic/gin"
    "github.com/golang-jwt/jwt/v5"
)

// token 黑名单
var blacklist = make(map[string]bool)

// 互斥锁 保护对 blacklist 的并发访问
var blacklistMutex sync.Mutex

type JWT struct {
    JwtKey []byte
}

func NewJWT() *JWT {
    return &JWT{
        []byte(utils.JwtKey),
    }
}

type MyClaims struct {
    Username string `json:"username"`
    // ID       uint   `json:"id"`
    jwt.RegisteredClaims
}

// 定义错误
var (
    ErrTokenExpired     = errors.New("token已过期,请重新登录。")
    ErrTokenNotValidYet = errors.New("token无效,请重新登录。")
    ErrTokenMalformed   = errors.New("token不正确,请重新登录。")
    ErrTokenInvalid     = errors.New("这不是一个token,请重新登录。")
)

// CreateToken 生成token
func (j *JWT) CreateToken(claims MyClaims) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(j.JwtKey)
}

// 检查 Token 是否在黑名单中
func IsTokenBlacklisted(tokenString string) bool {
    blacklistMutex.Lock()
    defer blacklistMutex.Unlock()
    _, exists := blacklist[tokenString]
    return exists
}

// 将 Token 添加到黑名单中
func AddToBlacklist(tokenString string) {
    blacklistMutex.Lock()
    defer blacklistMutex.Unlock()
    blacklist[tokenString] = true
}

// ParserToken 解析token
func (j *JWT) ParserToken(tokenString string) error {
    token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
        return j.JwtKey, nil
    })
    // 验证token
    if token.Valid {
        return nil
    } else if errors.Is(err, jwt.ErrTokenMalformed) {
        return ErrTokenMalformed
    } else if errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenNotValidYet) {
        return ErrTokenExpired
    } else if errors.Is(err, jwt.ErrTokenSignatureInvalid) {
        return ErrTokenInvalid
    } else {
        return ErrTokenNotValidYet
    }
}

// JwtToken jwt中间件
// todo 优化此类代码
func JwtToken() gin.HandlerFunc {
    return func(c *gin.Context) {
        var code int
        tokenHeader := c.Request.Header.Get("Authorization")
        if tokenHeader == "" {
            code = errmsg.ERROR_TOKEN_EXIST
            c.JSON(http.StatusOK, gin.H{
                "code":  code,
                "msg": errmsg.GetErrMsg(code),
            })
            c.Abort()
            return
        }

        checkToken := strings.Split(tokenHeader, " ")
        if len(checkToken) == 0 {
            c.JSON(http.StatusOK, gin.H{
                "code":  code,
                "msg": errmsg.GetErrMsg(code),
            })
            c.Abort()
            return
        }

        // 前端传回的 token 不带 Bearer 字段
        // 下面这个 if 的验证可以注释掉

        // if len(checkToken) != 2 || checkToken[0] != "Bearer" {
        //     c.JSON(http.StatusOK, gin.H{
        //         "status":  code,
        //         "message": errmsg.GetErrMsg(code),
        //         "data":    "token格式不正确2",
        //         "token":   tokenHeader,
        //         "check":  checkToken[0],
        //     })
        //     c.Abort()
        //     return
        // }

        // 判断是否在黑名单中
        if IsTokenBlacklisted(tokenHeader) {
            c.JSON(http.StatusUnauthorized, gin.H{
                "code": 401,
                "msg":  ErrTokenExpired,
                "data": nil,
            })
            // 重定向到登录页
            // 但前端好像不接受这个响应？？？
            // c.Redirect(http.StatusTemporaryRedirect, "/user/login/loginByPhone")
            c.Abort()
            return
        }

        j := NewJWT()
        // 解析token
        err := j.ParserToken(checkToken[0])
        if err != nil {
            if errors.Is(err, ErrTokenExpired) {
                c.JSON(http.StatusOK, gin.H{
                    "code":  errmsg.ERROR,
                    "msg": "token授权已过期,请重新登录",
                    "data":    nil,
                })
                c.Abort()
                return
            }

            // 其他错误
            c.JSON(http.StatusOK, gin.H{
                "code":  errmsg.ERROR,
                "msg": err.Error(),
                "data":    nil,
            })
            c.Abort()
            return
        }

        //c.Set("username",)
        c.Next()
    }
}

// GetUsernameFromToken 从 token 中解析并返回用户名
func GetUserNameFromToken(tokenString string) (string, error) {
    claims := MyClaims{}
    _, err := jwt.ParseWithClaims(tokenString, &claims, func(token *jwt.Token) (interface{}, error) {
        return []byte(utils.JwtKey), nil
    })

    if err != nil {
        return "", err
    }

    return claims.Username, nil
}
